Skip to content

chore: Update Markdown formatting standards and add Copilot integration guidance#111

Merged
GingerGraham merged 2 commits into
mainfrom
docs/fixing-makrdown-linting
Apr 23, 2026
Merged

chore: Update Markdown formatting standards and add Copilot integration guidance#111
GingerGraham merged 2 commits into
mainfrom
docs/fixing-makrdown-linting

Conversation

@GingerGraham
Copy link
Copy Markdown
Owner

This pull request introduces support for "fail-fast" test execution, allowing the test runner to stop immediately after the first failing suite. This feature is integrated into the test runner script, the Makefile, and documentation, and is intended to speed up debugging and CI workflows. Additionally, the pull request updates Markdown table formatting to require aligned columns, and updates documentation and linting rules accordingly.

Fail-fast test execution:

  • Added --fail-fast (or -x) option to tests/run_tests.sh to stop after the first suite with failures; updated help output and logic to support this mode. [1] [2]
  • Added make test-fail-fast and make coverage-debug targets to the Makefile for running tests in fail-fast mode, including with kcov for coverage debugging. [1] [2] [3] [4]
  • Updated documentation (docs/testing.md, .claude/skills/running-tests/SKILL.md, and CONTRIBUTING.md) to describe the fail-fast workflow, how to use it, and its benefits for debugging. [1] [2] [3] [4] [5] [6]

Test runner enhancements:

  • Modified tests/run_tests.sh to propagate failed test details through parallel execution, ensuring that fail-fast and error reporting work correctly in both sequential and parallel modes. [1] [2] [3]

Markdown table formatting and linting:

  • Changed MarkdownLint MD060 rule from "spaced" to "aligned" in .markdownlint.yaml, requiring all table columns to be padded for vertical alignment.
  • Updated all documentation and skill files to use aligned table columns, and clarified the MD060 rule in AGENTS.md and .claude/skills/linting-code/SKILL.md. [1] [2] [3] [4] [5] [6] [7]

These changes improve the developer experience by making test failures easier to diagnose and ensuring consistent documentation formatting

Copilot AI review requested due to automatic review settings April 23, 2026 09:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a fail-fast mode to the Bash test runner (plus Make targets and docs) and tightens Markdown table formatting rules to require visually aligned columns across documentation/skills.

Changes:

  • Added --fail-fast / -x support to tests/run_tests.sh, plus related summary/parallel-result plumbing for failed test details.
  • Added make test-fail-fast and make coverage-debug to make fail-fast workflows easy to invoke.
  • Updated docs/skills and MarkdownLint configuration to require aligned Markdown table columns (MD060).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/run_tests.sh Implements --fail-fast flag, help text, sequential early-stop behavior, and parallel aggregation of failed details.
tests/test_junit_output.sh Adds tests intended to cover the new runner behavior (fail-fast + summary behavior).
Makefile Adds test-fail-fast and coverage-debug targets to run the runner in fail-fast mode (including under kcov).
docs/testing.md Documents fail-fast usage and a suggested debugging workflow for make sonar-analysis failures.
CONTRIBUTING.md Updates the local tooling table to include the new Make targets.
AGENTS.md Updates documented MarkdownLint table rules (MD060) and related checklist formatting.
.markdownlint.yaml Switches MD060 configuration to require aligned table columns.
.claude/skills/writing-commits/SKILL.md Re-formats tables to satisfy aligned-column table formatting.
.claude/skills/security-review/SKILL.md Re-formats tables to satisfy aligned-column table formatting.
.claude/skills/running-tests/SKILL.md Documents fail-fast usage and re-formats tables for alignment.
.claude/skills/pre-pr-checks/SKILL.md Updates quick-reference table formatting (currently malformed).
.claude/skills/linting-code/SKILL.md Updates MarkdownLint guidance and re-formats tables for alignment.

Comment thread .claude/skills/pre-pr-checks/SKILL.md Outdated
Comment on lines +96 to +103
| Symptom | Cause | Fix | | | | |
| --------------------- | ---------------------------------- | ----------------------- | -------------------------- | ------ | --- | ------- |
| ShellCheck SC2086 | Unquoted variable | `"$var"` | | | | |
| ShellCheck SC2155 | Combined declare/assign | `local x; x=$(...)` | | | | |
| MarkdownLint MD004 | List marker is `-` | Change to `*` | | | | |
| Test not run | Call missing at bottom of suite | Add the call | | | | |
| Assertion not reached | Missing `\ | \ | return` on prior assertion | Add `\ | \ | return` |
| No release triggered | Commit type not release-triggering | Check `.releaserc.json` | | | | |
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This table appears malformed: it introduces extra empty columns and the “Assertion not reached” row is broken across cells (pipes/backslashes are being interpreted as table separators). This is likely to render incorrectly and may fail MarkdownLint table rules. Suggest reverting to a simple 3-column table and represent literals like || return using backticks within a single cell instead of trying to escape pipes with line breaks.

Suggested change
| Symptom | Cause | Fix | | | | |
| --------------------- | ---------------------------------- | ----------------------- | -------------------------- | ------ | --- | ------- |
| ShellCheck SC2086 | Unquoted variable | `"$var"` | | | | |
| ShellCheck SC2155 | Combined declare/assign | `local x; x=$(...)` | | | | |
| MarkdownLint MD004 | List marker is `-` | Change to `*` | | | | |
| Test not run | Call missing at bottom of suite | Add the call | | | | |
| Assertion not reached | Missing `\ | \ | return` on prior assertion | Add `\ | \ | return` |
| No release triggered | Commit type not release-triggering | Check `.releaserc.json` | | | | |
| Symptom | Cause | Fix |
| --------------------- | ---------------------------------- | ----------------------- |
| ShellCheck SC2086 | Unquoted variable | `"$var"` |
| ShellCheck SC2155 | Combined declare/assign | `local x; x=$(...)` |
| MarkdownLint MD004 | List marker is `-` | Change to `*` |
| Test not run | Call missing at bottom of suite | Add the call |
| Assertion not reached | Missing `|| return` on prior assertion | Add `|| return` |
| No release triggered | Commit type not release-triggering | Check `.releaserc.json` |

Copilot uses AI. Check for mistakes.
Comment thread AGENTS.md Outdated
* [ ] Commit message follows Conventional Commits format — see `writing-commits` skill
* [ ] Security implications reviewed for changes touching input, files, config, or external
commands — see `security-review` skill
commands — see `security-review` skill
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This list item continuation line is over-indented (6 spaces), which can make Markdown interpret it as a code block or nested content and may violate the repo’s list indentation rules. Align the continuation indentation with the rest of the checklist item text (typically 2 spaces after the bullet content begins).

Suggested change
commands — see `security-review` skill
commands — see `security-review` skill

Copilot uses AI. Check for mistakes.
Comment thread tests/run_tests.sh
Comment on lines +386 to +397
--fail-fast|-x)
FAIL_FAST=true
shift
;;
--help|-h)
echo "Usage: $0 [options] [test_suite...]"
echo ""
echo "Options:"
echo " --junit Generate JUnit XML report for CI/SonarQube"
echo " --output-dir DIR Directory for reports (default: ../test-reports)"
echo " -j, --parallel N Run N test suites in parallel (auto-detect cores by default, max 8)"
echo " -x, --fail-fast Stop after the first suite that contains a failure"
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--fail-fast is documented here as stopping after the first failing suite, but in the current parallel implementation the runner dispatches all suites up front and can’t detect a failure until after wait + aggregation. This means --fail-fast has no real effect when PARALLEL_JOBS > 1 (even though the help text implies it does). Consider either (1) making --fail-fast force PARALLEL_JOBS=1 / warn-and-disable parallelism, or (2) implementing true fail-fast in parallel by halting further dispatch once a failure is observed (e.g., via per-job status files + polling).

Copilot uses AI. Check for mistakes.
Comment thread docs/testing.md Outdated
Comment on lines +116 to +117
In parallel mode, the runner waits for all already-dispatched jobs to finish before printing
the summary and exiting.
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The note about parallel fail-fast behavior is misleading with the current runner implementation: in parallel mode run_tests.sh continues dispatching suites until all are launched, and only learns about failures after all jobs complete and results are aggregated. As written, --fail-fast won’t actually shorten a parallel run; consider clarifying that fail-fast is effective only with -j 1 / TEST_PARALLEL_JOBS=1, or updating the runner to stop dispatching new suites once a failure is detected.

Suggested change
In parallel mode, the runner waits for all already-dispatched jobs to finish before printing
the summary and exiting.
This is the only mode where `--fail-fast` actually shortens the run.
In parallel mode, the runner continues dispatching suites until all have been launched, then
waits for all jobs to finish and aggregates results before printing the summary and exiting.
In other words, `--fail-fast` does not currently stop a parallel run early; use `-j 1` or
`TEST_PARALLEL_JOBS=1` when you need true fail-fast behavior.

Copilot uses AI. Check for mistakes.
Comment thread tests/test_junit_output.sh Outdated
Comment on lines +382 to +392
# Run two suites with fail-fast; the first (log_levels) passes, so we need something
# that actually fails. We can't easily inject a failure here without a scratch suite,
# so instead verify the flag is accepted and the runner still exits cleanly when all
# suites pass (i.e., the flag doesn't break the happy path).
local output exit_code
output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh --fail-fast test_log_levels 2>&1)
exit_code=$?

assert_equals "0" "$exit_code" "--fail-fast should not affect a passing suite" || return
assert_contains "$output" "passed" || return

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test name/comment says it verifies that --fail-fast stops after the first failing suite, but the body only runs a single known-passing suite and asserts exit code 0. That doesn’t exercise the fail-fast behavior and could give false confidence. Consider creating a temporary failing suite file under tests/ during the test (and removing it afterward), then running ./tests/run_tests.sh --fail-fast -j 1 <failing_suite> <second_suite> and asserting the second suite never runs (e.g., its “Running …” line is absent).

Suggested change
# Run two suites with fail-fast; the first (log_levels) passes, so we need something
# that actually fails. We can't easily inject a failure here without a scratch suite,
# so instead verify the flag is accepted and the runner still exits cleanly when all
# suites pass (i.e., the flag doesn't break the happy path).
local output exit_code
output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh --fail-fast test_log_levels 2>&1)
exit_code=$?
assert_equals "0" "$exit_code" "--fail-fast should not affect a passing suite" || return
assert_contains "$output" "passed" || return
local unique_suffix failing_suite_name second_suite_name
local failing_suite_path second_suite_path
local failing_marker second_marker
local output exit_code second_marker_count
unique_suffix="fail_fast_$$"
failing_suite_name="test_${unique_suffix}_failing"
second_suite_name="test_${unique_suffix}_second"
failing_suite_path="$PROJECT_ROOT/tests/${failing_suite_name}.sh"
second_suite_path="$PROJECT_ROOT/tests/${second_suite_name}.sh"
failing_marker="FAIL_FAST_TEMP_FAILURE_MARKER_${unique_suffix}"
second_marker="FAIL_FAST_TEMP_SECOND_MARKER_${unique_suffix}"
cat >"$failing_suite_path" <<EOF
#!/usr/bin/env bash
echo "$failing_marker"
exit 1
EOF
cat >"$second_suite_path" <<EOF
#!/usr/bin/env bash
echo "$second_marker"
exit 0
EOF
chmod +x "$failing_suite_path" "$second_suite_path"
output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh --fail-fast -j 1 "$failing_suite_name" "$second_suite_name" 2>&1)
exit_code=$?
rm -f "$failing_suite_path" "$second_suite_path"
assert_equals "1" "$exit_code" "--fail-fast should exit non-zero after the first failing suite" || return
assert_contains "$output" "$failing_marker" "The failing suite should run before execution stops" || return
second_marker_count=$(printf '%s\n' "$output" | grep -F -c "$second_marker")
assert_equals "0" "$second_marker_count" "The second suite should not run when --fail-fast is enabled" || return

Copilot uses AI. Check for mistakes.
Comment thread tests/test_junit_output.sh Outdated
Comment on lines +412 to +424
# Run in parallel mode (default) against a known-passing suite.
# We can't easily inject a real failure here without a scratch file,
# so verify that a passing parallel run shows 0 in the Failed line
# (the summary section is populated correctly and isn't silent).
local output
output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh -j 2 test_log_levels 2>&1)

assert_contains "$output" "Failed:" "Summary should include Failed: line" || return
assert_contains "$output" "Passed:" || return

pass_test
}

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test claims to validate failed-test detail propagation for parallel runs, but it executes only a known-passing suite and then checks for generic summary headings (which would be present regardless). To actually cover the new FAILED_DETAILS aggregation path, consider generating a temporary suite that intentionally fails (using fail_test) and running it with -j 2, then asserting the output includes the expected entry in the “Failed Tests” section.

Suggested change
# Run in parallel mode (default) against a known-passing suite.
# We can't easily inject a real failure here without a scratch file,
# so verify that a passing parallel run shows 0 in the Failed line
# (the summary section is populated correctly and isn't silent).
local output
output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh -j 2 test_log_levels 2>&1)
assert_contains "$output" "Failed:" "Summary should include Failed: line" || return
assert_contains "$output" "Passed:" || return
pass_test
}
local suite_file suite_name output
suite_file=$(mktemp "$PROJECT_ROOT/tests/test_parallel_failed_details_XXXXXX.sh") || return
suite_name=$(basename "${suite_file%.sh}")
cat > "$suite_file" <<'EOF'
#!/usr/bin/env bash
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=tests/test_helpers.sh
source "$TEST_DIR/test_helpers.sh"
test_parallel_failed_details_temp_pass() {
start_test "Temporary passing test"
pass_test
}
test_parallel_failed_details_temp_fail() {
start_test "Intentional parallel failure"
fail_test "expected parallel failure details"
}
test_parallel_failed_details_temp_pass
test_parallel_failed_details_temp_fail
EOF
chmod +x "$suite_file" || {
rm -f "$suite_file"
return
}
output=$(cd "$PROJECT_ROOT" && ./tests/run_tests.sh -j 2 "$suite_name" 2>&1 || true)
rm -f "$suite_file"
assert_contains "$output" "Failed Tests" "Summary should include Failed Tests section" || return
assert_contains "$output" "Intentional parallel failure" "Summary should include the failed test entry" || return
assert_contains "$output" "expected parallel failure details" "Summary should include the propagated failure details" || return
pass_test
}

Copilot uses AI. Check for mistakes.
@GingerGraham
Copy link
Copy Markdown
Owner Author

@copilot apply changes based on the comments in this thread when looking at the tests and the --fail-fast logic we should aim to implement fail fast properly and not just document around the issue

- Force PARALLEL_JOBS=1 when --fail-fast is set so the runner
  actually stops after the first failing suite rather than waiting
  for all parallel jobs; remove dead post-aggregation fail-fast block
- Rewrite test_fail_fast_stops_after_first_failure to use real
  temporary failing/passing suites and assert the second suite never runs
- Rewrite test_parallel_failed_details_in_summary to use a real
  failing suite and assert failed-test details appear in summary
- Fix malformed table in pre-pr-checks/SKILL.md: clean 3-column layout
  with properly-aligned columns and escaped pipes (\|\|)
- Fix over-indented list continuation in AGENTS.md (6→2 spaces)
- Update docs/testing.md to document that --fail-fast forces sequential

Agent-Logs-Url: https://github.com/GingerGraham/bash-logger/sessions/dd62e1d5-7fc1-4654-b8f6-977cc6d71263

Co-authored-by: GingerGraham <34165628+GingerGraham@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 23, 2026

@copilot apply changes based on the comments in this thread when looking at the tests and the --fail-fast logic w...

Done in 96f12fb. Here's what was implemented:

Proper fail-fast (not a documentation workaround): --fail-fast now forces PARALLEL_JOBS=1 immediately after argument parsing, guaranteeing sequential execution and true early-exit behaviour. The dead "honour fail-fast after aggregation" block was removed.

Real tests for both behaviours:

  • test_fail_fast_stops_after_first_failure now creates two temporary suite files at runtime — one that calls fail_test, one that prints a marker — runs the runner with --fail-fast, and asserts: exit code is 1, the failing suite's output is present, and the second suite's marker is absent.
  • test_parallel_failed_details_in_summary now creates a temporary suite that calls fail_test and runs it with -j 2, then asserts the "Failed Tests" section and the specific failure message appear in the aggregated summary.

Formatting fixes: The malformed table in pre-pr-checks/SKILL.md was rewritten as a clean 3-column aligned table (using \|\| for the pipe literal). The over-indented continuation line in AGENTS.md was corrected from 6 spaces to 2. docs/testing.md now states that --fail-fast overrides any parallel-jobs setting.

@GingerGraham GingerGraham merged commit a89ef67 into main Apr 23, 2026
5 checks passed
@GingerGraham GingerGraham deleted the docs/fixing-makrdown-linting branch April 23, 2026 21:16
@GingerGraham
Copy link
Copy Markdown
Owner Author

🎉 This PR is included in version 2.5.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants